#include <iostream>
#include <string>
#include <unordered_map>
#include <sys/types.h>
#include <sys/socket.h>
#include "log_pro.hpp"
#include "socket.hpp"
#include <iconv.h>

uint16_t defaultPort = 10000;
int defaultFD = -1;
const int fdMaxNum = (sizeof(fd_set) * 8);

class Server
{
public:
    Server(int16_t port = defaultPort) : _port(port)
    {
        int opt = 1;
        setsockopt(_listenSock.getsock(), SOL_SOCKET, SO_REUSEADDR | SO_REUSEPORT, &opt, sizeof(opt)); // 设置服务器可以快速重启
        // 上面的设置要在bind之前，最好是创建socket的时候就进行setsockopt
        _listenSock.Bind(_port);
        _listenSock.Listen();
    }

    void toAccept(int listenFd)
    {
        std::string clientIp;
        uint16_t clientPort;
        int fd = _listenSock.Accept(clientIp, &clientPort);
        if (fd < 0)
        {
            lg(WARNING, "accept 失败!");
            return;
        }
        // accpet成功后，等于是读listenfd成功，把读到的fd放入fd_arr中
        int i = 0;
        for (; i < fdMaxNum; i++)
        {
            if (_fd_arr[i] == defaultFD) // 找到fd_arr中空位置，放入fd
            {
                _fd_arr[i] = fd;
                lg(DEBUG, "成功接收新连接:%d", fd);
                break;
            }
        }
        if (i == fdMaxNum)
        {
            lg(WARNING, "内核中fd_set被占满了,无法继续添加新链接");
        }
    }

    void toResolve(int fd, int max) // max是需要处理的最大的文件fd
    {
        char buffer[1024];
        int n = read(fd, buffer, sizeof(buffer) - 1);
        if (n < 0)
        {
            lg(WARNING, "read错误");
        }
        else if (n == 0) // 对端关闭
        {
            lg(NOTICE, "客户端:%d 断开连接", fd);
            for (int i = 0; i < max; i++) // 找出这个文件描述符在_fd_arr中的位置
            {
                if (_fd_arr[i] == fd)
                {
                    _fd_arr[i] = defaultFD; // 重置为默认fd
                }
            }
            close(fd); // 关闭这个文件描述符断开连接
        }
        else
        { // 正常的处理情况
            buffer[n - 2] = 0;
            std::cout << "getMassage: " << buffer << std::endl;
        }
    }

    void Distribute(fd_set *rset, int max) // 处理已经准备就绪的文件,这个max就是需要处理的最大的文件fd
    {
        for (int i = 0; i < max + 1; i++) // 寻找出输入输出型参数fd_set中的fd
        {
            if (true == FD_ISSET(i, rset))
            {
                if (i == _listenSock.getsock())
                {
                    toAccept(i);
                }
                else
                {
                    // lg(DEBUG,"处理用户链接...");
                    toResolve(i, max);
                }
            }
        }
    }

    void start()
    {
        int listenFd = _listenSock.getsock();
        for (int i = 0; i < fdMaxNum; i++) // 将需要进行select的fd数组初始化
        {
            _fd_arr[i] = defaultFD;
        }
        _fd_arr[0] = listenFd; // 将listenfd设置进入fd_arry
        fd_set rset;           // 创建内核数据结构fd_set位图
        while (true)           // 开始循环进行select
        {
            FD_ZERO(&rset); // 要在循环中清空内核的fd_set
            // 准备工作
            int max = _fd_arr[0];
            for (int i = 0; i < fdMaxNum; i++) // 将fd_arry中的数据放入select的set中
            {
                if (_fd_arr[i] > max) // 选出最大的需要被select的fd，之后select要用
                {
                    max = _fd_arr[i];
                }
                if (_fd_arr[i] != defaultFD)
                {
                    FD_SET(_fd_arr[i], &rset);
                }
                else
                    continue;
            }

            // debug,看看需要被select的文件有哪些
            // lg(DEBUG, "最大fd为:%d", max);
            // for (int i = 0; i < fdMaxNum; i++)
            // {
            //     if (_fd_arr[i] != defaultFD)
            //     {
            //         std::cout << _fd_arr[i] << " ";
            //     }
            // }
            // std::cout << std::endl<< "-------------------------" << std::endl<<std::endl;

            // 准备工作完成，进行select
            ///*struct*/ timeval timeout = {5, 0}; // 设置timeout时间，也是一开始的等待时间，如果使用这个timeval之后也会被输入输出型参数修改

            int ret_select = select(max + 1, &rset, nullptr, nullptr, nullptr); // 最后一个参数为null代表阻塞等待

            // debug
            // lg(DEBUG,"查看内核中fd_set");
            // for (int i = 0; i < fdMaxNum; i++)
            // {
            //     if (FD_ISSET(i, &rset) == true)
            //     {
            //         std::cout << i << " ";
            //     }
            // }
            // std::cout << std::endl<< "-------------------------" << std::endl<<std::endl;

            if (ret_select == 0) // 代表位图set中没有一个文件准备就绪
            {
                lg(NOTICE, "文件未就绪...");
            }
            else if (ret_select < 0) // 代表文件读取错误了
            {
                lg(ERROR, "文件读取错误 errno:%d %s", errno, strerror(errno));
                exit(-1);
            }
            else // 读取正确，开始处理准备就绪的文件
            {
                lg(DEBUG, "开始分配就绪文件");
                Distribute(&rset, max);
            }
        }
    }

private:
    Socket _listenSock;
    int16_t _port;
    int _fd_arr[fdMaxNum];
};